package lac.serviceuint;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import lac.model.core.persistent.Session;
import lac.model.core.persistent.SessionFactory;
import lac.serviceuint.cfg.Logic;
import lac.serviceuint.cfg.Method;
import lac.serviceuint.handler.LogicHandler;
import lac.serviceuint.handler.MethodHandler;
import lac.serviceuint.interceptor.LogicInterceptor;
import lac.serviceuint.interceptor.MethodInterceptor;
import lac.serviceuint.tool.HttpUtil;

public class ServiceUnitExecuter {

	private static Logger log = LoggerFactory.getLogger(ServiceUnitExecuter.class);
	
	@Autowired(required=false)
	private SessionFactory sessionFactory;
	
	@Autowired
	private ServiceUnitContext serviceUnitContext;
	
	/**
	 * 
	 * @param serviceUnitName
	 * @param methodName
	 * @param params
	 * @return
	 * @throws Exception 
	 */
	public MethodExecuteMessage doExecute(String serviceUnitName,String methodName,Object... params) throws Exception{
		MethodExecuteMessage message = new MethodExecuteMessage();
		before();
		List<MethodInterceptor>  methodInterceptors =ServiceUnitContext.methodInterceptorsContext.get();
		MethodHandler methodHandler = getMethodHandler(serviceUnitName,methodName);
		boolean isPass = doMethodInterceptors(true,methodInterceptors,methodHandler.getMethod(),params,null);
		Object returnValue = null;
		Exception methodException = null;
		if(isPass){
			try{
				returnValue = execute(serviceUnitName, methodName, params);
			}catch (Exception e) {
				methodException = e;
			}
		}
		doMethodInterceptors(false,methodInterceptors,methodHandler.getMethod(),params,methodException);
		Map<Object,Object> internalVariable = ServiceUnitContext.varMapContext.get();
		message.setReturnValue(returnValue);
		message.setInternalVar(internalVariable);
		Map<String,Object> paramMap = (Map<String, Object>) ServiceUnitContext.methodParamsMapContext.get();
		if(paramMap != null){
			message.setParams(paramMap.values().toArray());
		}
		after();
		if(methodException != null){
			throw methodException;
		}
		return message;
	}
	
	/**
	 * 
	 * @param serviceUnitName
	 * @param methodName
	 * @param dataList
	 * @return
	 * @throws Exception 
	 */
	public Object execute(String serviceUnitName,String methodName,Object... params) throws Exception{
		List<MethodInterceptor>  methodInterceptors = serviceUnitContext.getMethodInterceptors();
		MethodHandler methodHandler = getMethodHandler(serviceUnitName,methodName);
		boolean isPass = doMethodInterceptors(true,methodInterceptors,methodHandler.getMethod(),params,null);
		Object o = null;
		Exception methodException = null;
		if(isPass){
			try{
				o = call(serviceUnitName,methodName,params);
			}catch (Exception e) {
				methodException = e;
			}
		}
		doMethodInterceptors(false,methodInterceptors,methodHandler.getMethod(),params,methodException);
		if(methodException != null){
			throw methodException;
		}
		return o;
	}
	/**
	 * 
	 * @param serviceUnitName
	 * @param methodName
	 * @param params
	 * @return
	 * @throws Exception
	 */
	public Object call(String serviceUnitName,String methodName,Object... params) throws Exception{
		before();
		MethodHandler methodHandler = getMethodHandler(serviceUnitName,methodName);
		Object o = null;
		try{
			if(methodHandler != null){
				o = invoke(methodHandler,params);
			}else{
				log.error(serviceUnitName+"#"+methodName+" 没被加载！");
				throw new RuntimeException(serviceUnitName+"#"+methodName+" 没被加载！");
			}
		}finally {
			after();
		}
		
		return o;
	}
	
	public Object call(String serviceId,String serviceUnitName,String methodName,Object... params) throws Exception{
		return call(serviceId,serviceUnitName,methodName,null,params);
	}
	
	public Object call(String serviceId,String serviceUnitName,String methodName,String group,Object... params) throws Exception{
		Object paramsMap =ServiceUnitContext.methodParamsMapContext.get();
		Object o = proxy(serviceId,serviceUnitName,methodName,group,params);
		ServiceUnitContext.methodParamsMapContext.set(paramsMap);
		return o;
	}
	
	private MethodHandler getMethodHandler(String serviceUnitName,String methodName){
		String serviceUnitMethodKey = serviceUnitName+"_"+methodName;
		Map<String, MethodHandler> serviceUnitMethodContext = serviceUnitContext.getServiceUnitMethodContext();
		MethodHandler methodHandler = serviceUnitMethodContext.get(serviceUnitMethodKey);
		return methodHandler;
	}
	
	private Object invoke(MethodHandler methodHandler,Object... params)throws Exception{
		long  invokeStartTime = System.currentTimeMillis();
		log.debug("  ->"+methodHandler.getMethod().getServiceUnit().getName()+"#"+methodHandler.getMethod().getName()+"方法被调用。");
		ServiceUnitContext.methodParamsMapContext.set(methodHandler.createParamMap(params));
	
		List<LogicHandler> logicHandlers = methodHandler.getLogicHandlers();
		if(logicHandlers != null){
			List<LogicInterceptor>  logicInterceptors =serviceUnitContext.getLogicInterceptors();
			ServiceUnitContext.logicHandlerControlSignalContext.set(new LogicHandlerControlSignal());
			for(LogicHandler handler : logicHandlers){
				LogicHandlerControlSignal controlSignal = ServiceUnitContext.logicHandlerControlSignalContext.get();
				if(controlSignal.isReturn()){
					break;
				}
				if(controlSignal.getGoToLogicHandlerName() != null && !controlSignal.equals("")){
					if(controlSignal.getGoToLogicHandlerName() != null && !controlSignal.getGoToLogicHandlerName().equals(handler.getLogic().getName())){
						continue;
					}else{
						controlSignal.setGoToLogicHandlerName(null);
					}
				}
				long  startTime = System.currentTimeMillis();
				log.debug("    -->>"+handler.getLogic().getJavaClassName()+"开始执行。");
				boolean isPassLogic =doLogicInterceptors(true,logicInterceptors,handler.getLogic());
				if(isPassLogic){
					handler.execute();
				}

				doLogicInterceptors(false,logicInterceptors,handler.getLogic());
				long endTime = System.currentTimeMillis();
				log.debug("    <<--"+handler.getLogic().getJavaClassName()+"执行结束。时间："+String.valueOf(endTime-startTime)+"毫秒");
			}
		}
		
		long invokeEndTime = System.currentTimeMillis();
		log.debug("  <-"+methodHandler.getMethod().getServiceUnit().getName()+"#"+methodHandler.getMethod().getName()+"方法调用结束。时间："+String.valueOf(invokeEndTime-invokeStartTime)+"毫秒");
		Object result  = ServiceUnitContext.resultContext.get();
		return result;
	}
	
	/*@Deprecated
	private Object invoke(String serviceUnitName,String methodName,Object... params)throws Exception{
		String serviceUnitMethodKey = serviceUnitName+"_"+methodName;
		Map<String, MethodHandler> serviceUnitMethodContext = serviceUnitContext.getServiceUnitMethodContext();
		MethodHandler methodHandler = serviceUnitMethodContext.get(serviceUnitMethodKey);
		if(methodHandler != null){
			long  methodStartTime = System.currentTimeMillis();
			log.debug("  ->"+serviceUnitName+"#"+methodName+"方法被调用。");
			ServiceUnitContext.methodParamsMapContext.set(methodHandler.createParamMap(params));
			List<MethodInterceptor>  methodInterceptors =ServiceUnitContext.methodInterceptorsContext.get();
			List<MethodInterceptor>  methodInterceptors_pre = new ArrayList<>(methodInterceptors);
			List<MethodInterceptor>  methodInterceptors_post = new ArrayList<>(methodInterceptors);
			ServiceUnitContext.methodInterceptorsContext.set(new ArrayList<>());
			boolean isPass = doMethodInterceptors(true,methodInterceptors_pre,methodHandler.getMethod(),params,null);
			if(isPass){
				Exception methodException = null;
				try{
					List<LogicHandler> logicHandlers = methodHandler.getLogicHandlers();
					if(logicHandlers != null){
						List<LogicInterceptor>  logicInterceptors =serviceUnitContext.getLogicInterceptors();
						ServiceUnitContext.logicHandlerControlSignalContext.set(new LogicHandlerControlSignal());
						for(LogicHandler handler : logicHandlers){
							LogicHandlerControlSignal controlSignal = ServiceUnitContext.logicHandlerControlSignalContext.get();
							if(controlSignal.isReturn()){
								break;
							}
							if(controlSignal.getGoToLogicHandlerName() != null && !controlSignal.equals("")){
								if(controlSignal.getGoToLogicHandlerName() != null && !controlSignal.getGoToLogicHandlerName().equals(handler.getLogic().getName())){
									continue;
								}else{
									controlSignal.setGoToLogicHandlerName(null);
								}
							}
							long  startTime = System.currentTimeMillis();
							log.debug("    -->>"+handler.getLogic().getJavaClassName()+"开始执行。");
							boolean isPassLogic =doLogicInterceptors(true,logicInterceptors,handler.getLogic());
							if(isPassLogic){
								handler.execute();
							}
	
							doLogicInterceptors(false,logicInterceptors,handler.getLogic());
							long endTime = System.currentTimeMillis();
							log.debug("    <<--"+handler.getLogic().getJavaClassName()+"执行结束。时间："+String.valueOf(endTime-startTime)+"毫秒");
						}
					}
				}catch (Exception e) {
					methodException = e;
				}
				doMethodInterceptors(false,methodInterceptors_post,methodHandler.getMethod(),params,methodException);
				if(methodException != null){
					throw methodException;
				}
				long methodEndTime = System.currentTimeMillis();
				log.debug("  <-"+serviceUnitName+"#"+methodName+"方法调用结束。时间："+String.valueOf(methodEndTime-methodStartTime)+"毫秒");
			}
			
		}else{
			log.error(serviceUnitName+"#"+methodName+" 没被加载！");
			throw new RuntimeException(serviceUnitName+"#"+methodName+" 没被加载！");
		}
		Object result  = ServiceUnitContext.resultContext.get();
		
		
		return result;
	}*/
	
	
	public void before(){
		if(sessionFactory!= null){
			Map<Class<? extends Session>,Session> sessionMap = new HashMap<Class<? extends Session>,Session>();
			sessionMap.put(sessionFactory.getSession().getClass(), sessionFactory.getSession());
			sessionMap.put(sessionFactory.getCustumSession().getClass(), sessionFactory.getCustumSession());
			ServiceUnitContext.sessionContext.set(sessionMap);
		}
		ServiceUnitContext.methodInterceptorsContext.set(new ArrayList<>(serviceUnitContext.getMethodInterceptors()));
		
	}
	
	
	public void after(){
		ServiceUnitContext.sessionContext.remove();
		ServiceUnitContext.varMapContext.remove();
		ServiceUnitContext.methodParamsMapContext.remove();
		ServiceUnitContext.methodInterceptorsContext.remove();
		ServiceUnitContext.resultContext.remove();
	}
	
	
	private boolean doMethodInterceptors(boolean isPre,List<MethodInterceptor>  methodInterceptors,Method method,Object[] params,Exception e){
		boolean value = true;
		if(methodInterceptors != null){
			for(MethodInterceptor interceptor : methodInterceptors){
				if(isPre){
					value=interceptor.preMethodHandle(method,params);
				}else{
					interceptor.postMethodHandle(method,params,e);
				}
				
			}
		}
		return value;
	}
	
	private boolean doLogicInterceptors(boolean isPre,List<LogicInterceptor> logicInterceptors,Logic logic){
		boolean value = true;
		if(logicInterceptors != null){
			for(LogicInterceptor interceptor : logicInterceptors){
				if(isPre){
					value=interceptor.preLogicHandle(logic);
				}else{
					interceptor.postLogicHandle(logic);
				}
				
			}
		}
		return value;
	}
	
	private Object proxy(String serviceId,String serviceUnitName,String methodName,String group,Object... params){
		List<Object> serviceParams = new ArrayList<>();
		serviceParams.add(serviceId);
		serviceParams.add(serviceUnitName);
		serviceParams.add(methodName);
		serviceParams.add(group);
		serviceParams.add(serviceUnitContext.getServiceId());
		String[] registerAddress =  serviceUnitContext.getRegisterAddress().split(",");
		Random random = new Random();
		int index = random.nextInt(registerAddress.length);
		Object url =  HttpUtil.httpPostServiceUnitRequest(registerAddress[index]+"/RegisterService/getMethodHostUrl", serviceParams);
		
		if(url ==null){
			throw new RuntimeException("获取注册服务"+serviceId+"的方法"+serviceUnitName+"_"+methodName+"失败");
		}
		else if(url != null && url.equals(false)){
			throw new RuntimeException("获取注册服务"+serviceId+"的方法"+serviceUnitName+"_"+methodName+"失败");
		}else{
			String requestUrl = url.toString();
			List<Object> requestParams = new ArrayList<>();
			if(params != null){
				for(Object param : params){
					requestParams.add(param);
				}
			}
			Object data =  HttpUtil.httpPostServiceUnitRequest("http://"+requestUrl, requestParams);
			if(data != null && data.equals(false)){
				log.error(requestUrl+"请求失败!");
			}
			return data;
		}
	}
	

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	
	
}
